package com.workingpub.commons.orm.internal;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;

import com.workingpub.commons.domain.AutoIncrementIdentifier;
import com.workingpub.commons.domain.Identifiable;
import com.workingpub.commons.jdbc.JdbcManager;
import com.workingpub.commons.jdbc.internal.MySqlJdbcManager;
import com.workingpub.commons.jdbc.mapping.SqlMappingMapper;
import com.workingpub.commons.orm.EntityManager;
import com.workingpub.commons.orm.JPAMappingBuilder;
import com.workingpub.commons.orm.SqlBuildEntry;

/**
 * 
 * @author taoping
 *
 */
public class JdbcEntityManager implements EntityManager {
	private static final Logger logger = LoggerFactory.getLogger(JdbcEntityManager.class);
	private static final String QueryID_For_MySQL = "SELECT LAST_INSERT_ID()";

	private JdbcManager jdbcManager;

	@Override
	public <P extends Serializable, T extends Identifiable<P>> T get(Class<T> clazz, P id) {
		if (null == clazz || null == id) {
			return null;
		}
		try {
			T instance = clazz.newInstance();
			instance.setId(id);
			JPAMappingBuilder builder = new SelectBuilder(clazz);
			List<SqlBuildEntry> sqlEntries = builder.build(instance);
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return null;
			}
			return jdbcManager.queryForObject(sqlEntries.get(0).getSqlContext().toString(),
					sqlEntries.get(0).getSqlParameters().map(), new SqlMappingMapper<T>(clazz));
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <T extends Identifiable<?>> List<T> getAll(Class<T> clazz) {
		if (null == clazz) {
			return null;
		}
		try {
			T instance = clazz.newInstance();
			JPAMappingBuilder builder = new SelectBuilder(clazz);
			List<SqlBuildEntry> sqlEntries = builder.build(instance);
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return null;
			}
			return jdbcManager.queryForList(sqlEntries.get(0).getSqlContext().toString(),
					sqlEntries.get(0).getSqlParameters().map(), new SqlMappingMapper<T>(clazz));
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <T extends Identifiable<?>> int save(T entity) {
		if (null == entity) {
			return 0;
		}
		try {
			JPAMappingBuilder builder = new InsertBuilder(entity.getClass());
			List<SqlBuildEntry> sqlEntries = builder.build(entity);
			int rows = 0;
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return rows;
			}
			for (SqlBuildEntry entry : sqlEntries) {
				rows += jdbcManager.update(entry.getSqlContext().toString(), entry.getSqlParameters().map());
			}

			if (AutoIncrementIdentifier.class.isAssignableFrom(entity.getClass())) {
				Class<?> managerType = jdbcManager.getClass();
				if (AopUtils.isAopProxy(jdbcManager)) {
					managerType = AopUtils.getTargetClass(jdbcManager);
				}
				if (MySqlJdbcManager.class.equals(managerType)) {
					Long id = jdbcManager.queryForLong(QueryID_For_MySQL, (Map<String, Object>) null);
					logger.debug("LAST_INSERT_ID : " + id);
					((AutoIncrementIdentifier) entity).setId(id);
				}
			}
			return rows;
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <T extends Identifiable<?>> int save(T[] entities) {
		if (null == entities || entities.length == 0) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += save(entry);
		}
		return rows;
	}

	@Override
	public <T extends Identifiable<?>> int save(Collection<T> entities) {
		if (null == entities || entities.isEmpty()) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += save(entry);
		}
		return rows;
	}

	@Override
	public <T extends Identifiable<?>> int update(T entity) {
		try {
			JPAMappingBuilder builder = new UpdateBuilder(entity.getClass());
			List<SqlBuildEntry> sqlEntries = builder.build(entity);
			int rows = 0;
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return rows;
			}
			for (SqlBuildEntry entry : sqlEntries) {
				rows += jdbcManager.update(entry.getSqlContext().toString(), entry.getSqlParameters().map());
			}
			return rows;
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <T extends Identifiable<?>> int update(T[] entities) {
		if (null == entities || entities.length == 0) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += update(entry);
		}
		return rows;
	}

	@Override
	public <T extends Identifiable<?>> int update(Collection<T> entities) {
		if (null == entities || entities.isEmpty()) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += update(entry);
		}
		return rows;
	}

	@Override
	public <T extends Identifiable<?>> int saveOrUpdate(T entity) {
		int rows = update(entity);
		return 0 == rows ? save(entity) : rows;
	}

	@Override
	public <T extends Identifiable<?>> int delete(T entity) {
		try {
			JPAMappingBuilder builder = new DeleteBuilder(entity.getClass());
			List<SqlBuildEntry> sqlEntries = builder.build(entity);
			int rows = 0;
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return rows;
			}
			for (SqlBuildEntry entry : sqlEntries) {
				rows += jdbcManager.update(entry.getSqlContext().toString(), entry.getSqlParameters().map());
			}
			return rows;
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <T extends Identifiable<?>> int delete(T[] entities) {
		if (null == entities || entities.length == 0) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += delete(entry);
		}
		return rows;
	}

	@Override
	public <T extends Identifiable<?>> int delete(Collection<T> entities) {
		if (null == entities || entities.isEmpty()) {
			return 0;
		}
		int rows = 0;
		for (Identifiable<?> entry : entities) {
			rows += delete(entry);
		}
		return rows;
	}

	@Override
	public <P extends Serializable, T extends Identifiable<P>> int deleteById(Class<T> clazz, P id) {
		try {
			T instance = clazz.newInstance();
			instance.setId(id);
			return delete(instance);
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public <P extends Serializable, T extends Identifiable<P>> int deleteById(Class<T> clazz, P[] ids) {
		if (null == clazz || ids.length == 0) {
			return 0;
		}
		int rows = 0;
		for (P entry : ids) {
			rows += deleteById(clazz, entry);
		}
		return rows;
	}

	@Override
	public <P extends Serializable, T extends Identifiable<P>> int deleteById(Class<T> clazz, Collection<P> ids) {
		if (null == clazz || ids.isEmpty()) {
			return 0;
		}
		int rows = 0;
		for (P entry : ids) {
			rows += deleteById(clazz, entry);
		}
		return rows;
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T extends Identifiable<?>> List<T> findByExample(T example) {
		if (null == example) {
			return null;
		}
		try {
			JPAMappingBuilder builder = new SelectBuilder(example.getClass());
			List<SqlBuildEntry> sqlEntries = builder.build(example);
			if (null == sqlEntries || sqlEntries.isEmpty()) {
				return null;
			}
			return jdbcManager.queryForList(sqlEntries.get(0).getSqlContext().toString(),
					sqlEntries.get(0).getSqlParameters().map(), new SqlMappingMapper<T>((Class<T>) example.getClass()));
		} catch (Exception e) {
			logger.error("Save entity error : " + e.getMessage());
			throw new IllegalArgumentException(e);
		}
	}

	public void setJdbcManager(JdbcManager jdbcManager) {
		this.jdbcManager = jdbcManager;
	}
}
